<template>
	<view v-if="enable">
		<view class="vi_imageViewer" :class="{'vi_imageViewer_act':showViewer}" :style="mainStyle" @touchmove.stop.prevent @click="close">
			<view class="vi_imageViewer_mask">
				<view class="vi_imageViewer_title" :class="titleClass||''" :style="titleStyle">{{currentTitle}}</view>
				<swiper class="vi_imageViewer_swiper" :style="swiperStyle"
					:current="current"
					:autoplay="playing"
					:interval="setting.interval"
					:duration="setting.duration"
					:circular="setting.circular"
					@change="swiperChange">
					<swiper-item v-for="(item, index) in imageList" :key="index">
						<image class="vi_image" :src="item.image" :mode="setting.imgMode" @click.stop="openLarge()"/>
					</swiper-item>
				</swiper>
				<!-- 工具条 -->
				<view class="vi_imageViewer_toolbar" :style="toolbarStyle" @click.stop>
					<view class="vi_imageViewer_group">
						<text class="iconfont" :class="iconSetting.prev" @click.stop.prevent="turnPage('prev', true)"></text>
					</view>
					<view class="vi_imageViewer_group vi_imageViewer_page">
						<text>{{ current + 1 }} / {{ imageList.length }}</text>
					</view>
					<view class="vi_imageViewer_group">
						<text class="iconfont" :class="iconSetting.next" @click.stop.prevent="turnPage('next', true)"></text>
					</view>
				</view>
				<!-- 图片描述 -->
				<view class="vi_imageViewer_describe" :class="[desClass||'', {'vi_imageViewer_act':desOpened}]" :style="desStyle" @click.stop>
					<scroll-view class="vi_imageViewer_view" scroll-y @touchmove.stop>
						<view v-html="currentDescribe"></view>
					</scroll-view>
					<view class="vi_imageViewer_desSwitch" :class="{'vi_imageViewer_hide':!currentDescribe}" @touchmove="slideMove" @touchstart="slideStart" @click.stop="slideClick()">
						<view class="vi_imageViewer_btnbox">
							<view class="vi_imageViewer_btn iconfont" :class="iconSetting.down"></view>
						</view>
					</view>
				</view>
			</view>
		</view>
		
		<!-- 放大器 -->
		<vi-image-enlarge ref="imageEnlarger" v-if="setting.useEnlarge"
			:dataSet="currentImage"
			:describe="currentDescribe"
			:bacgroundColor="enlargeSetting.bacgroundColor"
			:min="enlargeSetting.min"
			:max="enlargeSetting.max"
			:step="enlargeSetting.step"
			:screenFix="enlargeSetting.screenFix"
			:animations="enlargeSetting.animation"
			:space="enlargeSetting.space"
			:toolbar="enlargeSetting.toolbar"
			:buttonSize="enlargeSetting.btnSize"
			:buttonScale="enlargeSetting.btnScale"
			:buttonRotate="enlargeSetting.btnRotate"
			:buttonTurn="enlargeSetting.btnTurn"
			:buttonPage="enlargeSetting.btnPage&&pages"
			:buttonSave="enlargeSetting.btnSave"
			:icon="enlargeSetting.icon"
			:buttonCustom="enlargeSetting.btnCustom"
			:params="enlargeSetting.params"
			:longPressTime="enlargeSetting.longPressTime"
			:maskClick="enlargeSetting.maskClick"
			:desClass="enlargeSetting.desClass"
			:desFontSize="enlargeSetting.desFontSize"
			:desMaxRow="enlargeSetting.desMaxRow"
			:desLineHeight="enlargeSetting.desLineHeight"
			:desTrigger="enlargeSetting.desTrigger"
			:autoCenter="enlargeSetting.autoCenter"
			@prev="turnPage('prev')"
			@next="turnPage('next')"
			@closed="eventClose"
			@click="eventClick"
			@longPress="eventLongPress"
			@event="event"></vi-image-enlarge>
	</view>
</template>

<script>
export default {
	name: 'viImageViewer', //插件名称
	props: {
		dataList:{
			type:[Array,String,Object,null],
			default:()=>[]
		},
		index:{
			type:[Number,null],
			default:0
		},
		//自动播放
		autoplay:{
			type:[Boolean,null],
			default:true
		},
		//自动切换时间间隔
		interval:{
			type:[Number,null],
			default:0
		},
		//滑动动画时长
		duration:{
			type:[Number,null],
			default:0
		},
		//衔接滑动
		circular:{
			type:[Boolean,null],
			default:null
		},
		//工具条按钮大小
		buttonSize:{
			type:[String,null],
			default:""
		},
		imgMode:{
			type:[String,null],
			default:""
		},
		//swiper高度。swiper不能自动撑高
		height:{
			type:[String,null],
			default:""
		},
		titleClass:{
			type:[String,null],
			default:""
		},
		titleFontSize:{
			type:[String,null],
			default:""
		},
		desClass:{
			type:[String,null],
			default:""
		},
		desFontSize:{
			type:[String,null],
			default:""
		},
		zIndex:{
			type:[Number,null],
			default:null
		},
		//下拉动作触发距离，单位px
		desTrigger:{
			type:[Number,null],
			default:0
		},
		//使用图片放大器
		useEnlarge:{
			type:[Boolean,null],
			default:null
		},
		//图片放大器配置参数，参照图片放大器说明
		enlargeSet:{
			type:[Object,null],
			default:()=>({
				// min:0.5,
				// max:4,
				// ……
			})
		},
		icon:{
			type:[Object,null],
			default:()=>({})
		}
	},
	data() {
		return {
			enable:false,
			showViewer: false,
			openTimer:null,		//打开计时器，用来确定提交事件
			closeTimer:null,	//关闭计时器，用来确定提交事件
			popDuration: 300,	//动画持续时长，须与css的transition变量一致
			imageList:[],
			current: 0, 		//当前页
			currentImage:"",	//当前图片地址
			currentDescribe:"",	//当前图片描述
			currentTitle:"",	//当前图片标题
			desOpened:false,
			desIsOpen:false,	//暂存变量，判断下拉触发时是否已打开
			desStart:0,			//暂存变量，下拉起始点y坐标
			pages:false,		//是否有多页
			screenW:0,
			screenH:0,
			playState:false,	//标志，判断关闭放大器后是否需继续播放
			playing:false,		//播放状态
			dicEnlargeSet:{},	//放大器默认设置，暂无
			dicIcon:{
				prev: "icon-prev",
				next: "icon-next",
				down: "icon-arr-down"
			}
		};
	},
	computed:{
		//将默认值内含到css、计算属性等地方，只内含真值
		//规避特殊使用的情况下，由prop传入undefined、null等无效数据导致的，默认值失效问题。例如组件嵌套且需要组件间参数传递
		//HBuilder X3.3.13; 微信开发者工具stable 1.05.2111300
		//微信小程序会对传入的undefined、null值根据参数类型输出不同空值(false、""、null、0)，参数类型为null或未指定参数类型则输出null
		//H5会对传入的undefined取默认值，未设默认值则输出undefined。对传入的null值固定输出null
		//做参数传递时需警惕两者行为不同而造成的陷阱，主要问题出现在微信小程序的空值转化，输出非用户传入的空值、假值，使默认真值丢失
		//兼容空值取默认值的可行方案目前为，参数类型加入null如[Boolean,null]，判断传入参数是否为定义的类型，是则使用，否则赋予默认值
		setting(){
			return {
				circular: this.$api.isBoolean(this.circular)? this.circular :true,
				useEnlarge: this.$api.isBoolean(this.useEnlarge)? this.useEnlarge :true,
				interval: this.interval ||5000,
				duration: this.duration ||500,
				buttonSize: this.buttonSize ||"32rpx",
				imgMode: this.imgMode ||"aspectFit",
				height: this.height ||"400rpx",
				desTrigger: this.desTrigger ||30,
				zIndex: this.$api.isValue(this.zIndex)?this.zIndex :998,
			}
		},
		mainStyle(){
			let res ={
				"z-index":this.setting.zIndex,
				"background-color":"rgba(0,0,0,0.4)",
			};
			return this.$api.objToStr(res)
		},
		swiperStyle(){
			return `height:${this.setting.height}`
		},
		toolbarStyle(){
			return `font-size:${this.setting.buttonSize};`
		},
		titleStyle(){
			return this.titleFontSize? `font-size:${this.titleFontSize};` :""
		},
		desStyle(){
			return this.desFontSize? `font-size:${this.desFontSize};` :""
		},
		enlargeSetting(){
			if(this.$api.isObject(this.enlargeSet)){
				return Object.assign({}, this.dicEnlargeSet, this.enlargeSet)
			}
			return this.dicEnlargeSet
		},
		iconSetting(){
			if(this.$api.isObject(this.icon)){
				return Object.assign({},this.dicIcon,this.icon)
			}
			return this.dicIcon
		}
	},
	methods:{
		//图片改变
		swiperChange(e) {
			this.current = e.target.current; //更新当前图片index
			this.getCurrentData();
			this.slideClick(true);
			this.$emit("change", this.current);
		},
		//打开预览器，数值表示切换当前swiper，url串查找并打开当前组内对应图片，找不到则打开单图放大器
		open(e) {
			if(e || e === 0){//有参数
				if(!isNaN(Number(e))){
					e = Number(e);
					if(e < 0 ||e >= this.imageList.length){
						console.log("imageViewer，open参数无效", e);
					}else{
						this.current = e;
						//打开swiper
						this.openFrame();
					}
				}else{//e为url
					let idx = this.$api.arrFind(this.imageList, {key:"image", value:e, format:"index"});
					if(idx == -1){//单图模式
						this.imageList = [{ "image":e }];//重置swiper列表
						this.current = 0;
						this.getCurrentData();//重置当前数据
						this.singleMod();
					}else{
						this.current = idx;
						//打开swiper
						this.openFrame();
					}
				}
			}else{//无参数，使用已初始化数据默认打开
				if(this.imageList.length == 1){//单图模式
					this.singleMod();
				}else{
					//多图开启翻页按钮
					this.pages = true;
					//打开swiper
					this.openFrame();
				}
			}
		},
		//打开执行
		openFrame(){
			//打开中或已打开，退出
			if((this.enable&&this.openTimer)||this.showViewer){
				return
			}
			//如果有关闭计时器，清除
			if(this.closeTimer){
				clearTimeout(this.closeTimer);
				this.closeTimer =null;
			}
			this.enable = true;
			//延迟执行动画，否则不显示动画只闪烁
			setTimeout(()=>{
				this.showViewer = true;
				this.openTimer = setTimeout(()=>{
					this.openTimer = null;
					this.$emit("opened")
				},this.popDuration)
			},30)
			//下一帧提交变更事件，确保内容已创建
			this.$nextTick(()=>{
				this.$emit("change",true)
			})
		},
		//关闭预览器
		close() {
			//关闭中或已关闭，退出
			if((!this.showViewer&&this.closeTimer)||!this.enable){
				return
			}
			//如果有打开计时器，清除
			if(this.openTimer){
				clearTimeout(this.openTimer);
				this.openTimer =null;
			}
			this.showViewer = false;
			//动画完成后卸载
			this.closeTimer = setTimeout(()=>{
				this.enable = false;
				this.closeTimer = null;
				//恢复现场
				let index = this.index||0;
				this.current = index<this.imageList.length? index :0;
				this.desOpened = false;
				this.getCurrentData();
				//如果是多图，则再向外抛出
				if(this.imageList.length > 1){
					this.$emit("closed");
				}
			},this.popDuration)
			//提交变更事件
			this.$emit("change",false);
		},
		//手动播放
		paly(){
			this.playing = this.playState = true
		},
		//手动停止
		stop(){
			this.playing = this.playState = false
		},
		//放大器关闭事件，单图模式抛出close事件，多图模式抛出enlargeClose事件
		eventClose(){
			if(this.imageList.length == 1){
				this.$emit("closed")
			}else{
				this.$emit("enlargeClose", this.current)
				//如果之前处于播放状态，则再次播放
				if(this.playState){
					this.playing = true
				}
			}
		},
		//放大器图片点击事件，传递向外抛出
		eventClick(){
			this.$emit("click", this.current)
		},
		eventLongPress(){
			this.$emit("longPress", this.current)
		},
		//自定义按钮点击事件
		event(e){
			this.$emit("event", Object.assign(e, {
				index: this.current,
				title: this.currentTitle,
				describe: this.currentDescribe
			}))
		},
		//单图模式
		singleMod(){
			this.close();//关闭swiper
			this.pages = false;//关闭放大器内部翻页按钮
			this.openLarge();
		},
		//打开放大器
		openLarge(){
			if(this.setting.useEnlarge){
				this.$refs.imageEnlarger.open();
				this.playing = false;//暂停播放
			}
		},
		//翻页
		turnPage(type, fromViewer){
			if(type == "prev"){
				if(this.current <= 0){
					if(this.setting.circular){
						this.current = this.imageList.length -1
					}else{
						this.current = 0
					}
				}else{
					this.current -= 1
				}
			}else if(type == "next"){
				if(this.current >= this.imageList.length -1){
					if(this.setting.circular){
						this.current = 0
					}else{
						this.current = this.imageList.length -1
					}
				}else{
					this.current += 1
				}
			}
			//重置停留时间
			this.playing = false;
			this.$nextTick(()=>{
				if(this.playState&&fromViewer){
					this.playing = true
				}
			})
		},
		//描述开关滑动开始
		slideStart(e){
			if (e.touches.length != 1) {
				return false;
			}
			this.desStart = e.touches[0].clientY;
			this.desIsOpen = this.desOpened;
		},
		slideMove(e){
			if (e.touches.length != 1) {
				return false;
			}
			let y = e.touches[0].clientY,
				sy = this.desStart;
			if(y>sy && y-sy>(this.setting.desTrigger)){
				this.desOpened = true
			}else if(y<sy && sy-y>(this.setting.desTrigger)){
				this.desOpened = false
			}
		},
		//打开描述
		slideClick(init){
			if(!this.currentDescribe){//无描述直接关闭
				this.desOpened = false
				return
			}
			if(init){//初始化仅判断是否有描述，不做动作
				return
			}
			this.desOpened = !this.desOpened
		},
		//提取图片数据集
		getData(){
			let list = this.dataList;
			if(!list){
				return
			}
			let type = this.$api.typeAsk(list);
			switch(type){
				case "Array":
					this.imageList = this.$api.clone(list);
					break;
				case "Object":
					this.imageList = [list];
					break;
				case "String":
					this.imageList = [
						{ image: list },
					];
					break;
			}
			this.getCurrentData();
		},
		//提取当前页数据
		getCurrentData(){
			let item = this.imageList[this.current];
			if(item){
				this.currentTitle = item.title ||"";
				this.currentImage = item.image ||"";
				this.currentDescribe = item.describe ||"";
			}else{
				this.currentTitle = "";
				this.currentImage = "";
				this.currentDescribe = "";
				console.log("index值错误，找不到图片对象");
			}
		},
		getScreen(){
			try {
			    const systemInfo = uni.getSystemInfoSync();
				this.screenW = systemInfo.windowWidth;
				this.screenH = systemInfo.windowHeight;
			} catch (e) {
			    this.screenW = this.screenH = 0
			}
		}
	},
	watch:{
		"dataList":{
			immediate: true,
			handler(newVal){
				this.getData();
			}
		},
		"index":{
			immediate: true,
			handler(newVal){
				this.current = newVal||0;
			}
		},
		"autoplay":{
			immediate: true,
			handler(newVal){
				newVal = this.$api.isBoolean(newVal)? newVal :true;
				this.playing = this.playState = newVal;
			}
		}
	}
};
</script>

<!--使用scss,只在本组件生效-->
<style lang="scss">
$desHeigh:6em;
$maskOpacity:.3;
$transition:.3s;
.vi_imageViewer{
	position: fixed;
	top: 0;
	left: 0;
	right: 0;
	bottom: 0;
	opacity: 0;
	transition: $transition;
	user-select: none;
	&.vi_imageViewer_act{
		opacity: 1;
	}
	.vi_imageViewer_mask{
		width: 100%;
		position: absolute;
		top: 50%;
		background-color: rgba(0,0,0,$maskOpacity);
		transform: translateY(-50%);
	}
	.vi_imageViewer_swiper{
		width: 100%;
		margin: auto;
		.vi_image{
			width: 100%;
		}
	}
	.vi_imageViewer_toolbar{
		display: flex;
		align-items: center;
		justify-content: center;
		position: relative;
		width: 100%;
		margin: .75em 0;
		color: rgba(255,255,255,0.75);
		.vi_imageViewer_group{
			background-color: rgba(0,0,0,0.25);
			padding: 0.25em 0.5em;
			border-radius: 0.35em;
			margin-right: 1em;
			flex-shrink: 0;
			&:last-child{
				margin-right: 0;
			}
		}
		.iconfont{
			margin-right: 0.5em;
			&:last-child{
				margin-right:0
			}
			&:active{
				color: rgba(255,255,255,1);
			}
			&.disabled{
				color: rgba(255,255,255,0.25);
			}
		}
		.vi_imageViewer_page{
			padding: 0.25em 0.75em;
			font-size: 80%;
			border-radius: 0.75em;
		}
	}
	.vi_imageViewer_title{
		text-align: center;
		line-height: 1.5;
		margin-bottom: .5em;
		padding: .75em 1em 0;
		color: rgba(255,255,255,0.75);
		white-space: nowrap;
		text-overflow: ellipsis;
		overflow: hidden;
	}
	.vi_imageViewer_describe {
		position: relative;
		color: rgba(255,255,255,0.75);
		height: 0;
		transition: height $transition;
		&.vi_imageViewer_act{
			height: $desHeigh;
			.vi_imageViewer_desSwitch .vi_imageViewer_btnbox{
				transform: rotate(180deg);
			}
		}
		.vi_imageViewer_view{
			width: 100%;
			height: calc(100% - 0.5em);
			padding: 0 1em .5em;
			box-sizing: border-box;
		}
		.vi_imageViewer_desSwitch{
			height: 1em;
			position: absolute;
			left: 50%;
			bottom: -1em;
			background-color: rgba(0,0,0,$maskOpacity);
			transform: translateX(-50%);
			transition: opacity $transition;
			&::before{
				content: "";
				position: absolute;
				top: 0;
				right: 100%;
				border-top: 1em solid rgba(0,0,0,$maskOpacity);
				border-left: 1em solid transparent;
			}
			&::after{
				content: "";
				position: absolute;
				top: 0;
				left: 100%;
				border-top: 1em solid rgba(0,0,0,$maskOpacity);
				border-right: 1em solid transparent;
			}
			&.vi_imageViewer_hide{
				opacity: 0
			}
			.vi_imageViewer_btnbox{
				transition: transform $transition;
				height: 100%;
				.vi_imageViewer_btn{
					width: 1em;
					line-height: 1;
					margin-top: -0.25em;
					padding: 0 1em;
					animation: vi-imageViewer-jump 0.5s infinite;
				}
			}
		}
	}

}
@keyframes vi-imageViewer-jump{
	0%{ transform: translateY(0); }
	50%{ transform: translateY(-0.25em); }
	100%{ transform: translateY(0); }
}
</style>